package cn.exrick.xboot.modules.activiti.serviceimpl.business;

import java.lang.reflect.Array;
import java.util.*;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import cn.exrick.xboot.common.constant.ActivitiConstant;
import cn.exrick.xboot.common.utils.SecurityUtil;
import cn.exrick.xboot.modules.activiti.dao.mapper.ActMapper;
import cn.exrick.xboot.modules.activiti.entity.ActBusiness;
import cn.exrick.xboot.modules.activiti.service.ActBusinessService;
import cn.exrick.xboot.modules.activiti.service.ActProcessService;
import cn.exrick.xboot.modules.activiti.service.mybatis.IHistoryIdentityService;
import cn.exrick.xboot.modules.activiti.vo.HistoricTaskVo;
import cn.exrick.xboot.modules.activiti.vo.ProcessNodeVo;
import cn.exrick.xboot.modules.activiti.vo.TaskVo;
import cn.exrick.xboot.modules.base.entity.Comment;
import cn.exrick.xboot.modules.base.entity.Department;
import cn.exrick.xboot.modules.base.entity.Role;
import cn.exrick.xboot.modules.base.entity.User;
import cn.exrick.xboot.modules.base.service.DepartmentService;
import cn.exrick.xboot.modules.base.service.UserService;
import cn.exrick.xboot.modules.base.service.mybatis.IUserRoleService;
import org.activiti.engine.HistoryService;
import org.activiti.engine.IdentityService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.history.HistoricIdentityLink;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.impl.persistence.entity.IdentityLinkEntity;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.IdentityLink;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.exrick.xboot.common.vo.SearchVo;
import cn.exrick.xboot.modules.activiti.dao.business.MatterDao;
import cn.exrick.xboot.modules.activiti.entity.business.Matter;
import cn.exrick.xboot.modules.activiti.service.business.MatterService;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 事项流转接口实现
 * @author lihuan
 */
@Slf4j
@Service
@Transactional
public class MatterServiceImpl implements MatterService {

    @Autowired
    private MatterDao matterDao;
    @Autowired
    private UserService userService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private SecurityUtil securityUtil;
    @Autowired
    private ActBusinessService actBusinessService;
    @Autowired
    private ActProcessService actProcessService;
    @Autowired
    private DepartmentService departmentService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private IHistoryIdentityService iHistoryIdentityService;
    @Autowired
    private ActMapper actMapper;
    @Autowired
    private IUserRoleService iUserRoleService;

    @Override
    public MatterDao getRepository() {
        return matterDao;
    }

    @Override
    public Page<Matter> findByCondition(Matter matter, SearchVo searchVo, Pageable pageable) {

        return matterDao.findAll(new Specification<Matter>() {
            @Nullable
            @Override
            public Predicate toPredicate(Root<Matter> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {

                // TODO 可添加你的其他搜索过滤条件 默认已有创建时间过滤
                Path<Date> createTimeField=root.get("createTime");
                Path<String> field0043Field=root.get("field0043");

                List<Predicate> list = new ArrayList<Predicate>();

                //创建时间
                if(StrUtil.isNotBlank(searchVo.getStartDate())&&StrUtil.isNotBlank(searchVo.getEndDate())){
                    Date start = DateUtil.parse(searchVo.getStartDate());
                    Date end = DateUtil.parse(searchVo.getEndDate());
                    list.add(cb.between(createTimeField, start, DateUtil.endOfDay(end)));
                }

                //是否协办
                if(StrUtil.isNotBlank(matter.getField0043())){
                    list.add(cb.equal(field0043Field,matter.getField0043()));
                }
                Predicate[] arr = new Predicate[list.size()];
                cq.where(list.toArray(arr));
                return null;
            }
        }, pageable);
    }
    private String getCurrentRole(String type){
        return this.getUserRole(securityUtil.getCurrUser(),type);
    }
    private String getUserRole(User user,String type){
        //获取当前用户角色
        List<String> compareName = null;
        if ("1".equals(type)){
            compareName = Arrays.asList("科室负责人","科长","科员","镇长","书记");
        }else{
            compareName = Arrays.asList("包村领导","包村组长","包村组员","村支书","镇长","书记");
        }
        //当前节点用户角色
        String compareRet = null;
        for (String compare : compareName){
            for (Role r : user.getRoles()){
                if (r.getName().indexOf(compare) >= 0){
                    return compare;
                }
            }
        }
        return null;
    }


    private List<User> getFilterUser(List<User> arr,String cmp){
        ArrayList<User> ret = new ArrayList<>();
        if (arr == null || arr.size() == 0) {
            return ret;
        }
        for (User u : arr){
            boolean find = false;
            if (u.getRoles() != null && u.getRoles().size() > 0){
                for (Role r : u.getRoles()){
                    if (r.getName().equals(cmp)){
                        find = true;
                        break;
                    }
                }
            }
            if (find){
                ret.add(u);
            }
        }
        return ret;
    }


    @Override
    public ProcessNodeVo getDelegateUsers(String procInstId) {
        ProcessNodeVo node = new ProcessNodeVo();
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(procInstId).singleResult();
        ActBusiness actBusiness = actBusinessService.get(pi.getBusinessKey());
        Matter matter = get(actBusiness.getTableId());

        String compareRet = getCurrentRole(matter.getField0018());
        if (compareRet == null){
            node.setUsers(Arrays.asList());
            return node;
        }

        Department department = departmentService.get(matter.getField0012());
        //获取承办村信息
        String departName = department.getTitle();

        List<User> tmp;
        List<User> nextDelegate = new ArrayList<>();
        switch (compareRet){
            case "包村领导":
                tmp= userService.findByRoleName("%\\_" + departName + "_包村组长");
                nextDelegate.addAll(tmp);
                break;
            case "包村组长":
                nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_包村组员"));
                break;
            case "包村组员":
//                nextDelegate.addAll(userService.findByDepartmentAndRoleName(securityUtil.getCurrUser().getDepartmentId(),"%村支书%"));
                break;
            case "村支书":
                nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_包村领导%"));
                break;
            case "镇长":
                nextDelegate.addAll(userService.findByRoleName("%\\_" + "南彩镇" + "_书记%"));
                break;
            case "书记":
                break;
            case "科室负责人":
                tmp= userService.findByRoleName("%\\_" + departName + "_科长");
                nextDelegate.addAll(tmp);
                break;
            case "科长":
                tmp= userService.findByRoleName("%\\_" + departName + "_科员");
                nextDelegate.addAll(tmp);
                break;
            case "科员":
                break;
        }

        node.setUsers(removeDuplicate(nextDelegate));
        return node;
    }

    @Override
    public ProcessNodeVo getPassUsers(String procInstId) {
        ProcessNodeVo node = new ProcessNodeVo();
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(procInstId).singleResult();
        ActBusiness actBusiness = actBusinessService.get(pi.getBusinessKey());
        Matter matter = get(actBusiness.getTableId());

        String compareRet = getCurrentRole(matter.getField0018());
        if (compareRet == null){
            node.setUsers(Arrays.asList());
            return node;
        }
        Department department = departmentService.get(matter.getField0012());
        String departName = department.getTitle();

        boolean needAddCreater = false;
        List<User> tmp;
        List<User> nextDelegate = new ArrayList<>();
        switch (compareRet){
            case "包村领导":
                ProcessNodeVo t = actProcessService.getNextNode(procInstId);
                nextDelegate.addAll(this.getFilterUser(t.getUsers(),"ROLE_" + "南彩镇" + "_镇长"));
                needAddCreater = true;
                break;
            case "包村组长":
                nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_包村领导%"));
                break;
            case "包村组员":
                nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_包村组长%"));
                break;
            case "村支书":
                nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_包村组员%"));
                break;
            case "镇长":
            case "书记":
                needAddCreater = true;
                break;
            case "科室负责人":
                ProcessNodeVo t2 = actProcessService.getNextNode(procInstId);
                nextDelegate.addAll(this.getFilterUser(t2.getUsers(),"ROLE_" + "南彩镇" + "_镇长"));
                needAddCreater = true;
                break;
            case "科长":
                nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_科室负责人%"));
                break;
            case "科员":
                nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_科长%"));
                break;
        }
        if (needAddCreater){
            nextDelegate.add(userService.get(actBusiness.getUserId()));
        }
        node.setUsers(removeDuplicate(nextDelegate));
        return node;
    }

    @Override
    public ProcessNodeVo getBackUsers(String procInstId,String nodeId) {
        ProcessNodeVo node = new ProcessNodeVo();
        boolean needAddCreater = false;
        List<User> nextDelegate = new ArrayList<>();
        ProcessInstance pi = runtimeService.createProcessInstanceQuery().processInstanceId(procInstId).singleResult();
        ActBusiness actBusiness = actBusinessService.get(pi.getBusinessKey());
        if ("-1".equals(nodeId)){
            needAddCreater = true;
        }else{
            Matter matter = get(actBusiness.getTableId());
            String compareRet = getCurrentRole(matter.getField0018());
            if (compareRet == null){
                node.setUsers(Arrays.asList());
                return node;
            }
            Department department = departmentService.get(matter.getField0012());
            String departName = department.getTitle();
            String preOptionType = null;
            List<Comment> allComment = actMapper.queryHistoricFlowAll(procInstId);
            preOptionType = this.findFrontCommentAction(allComment,0);

            switch (compareRet){
                case "包村领导":
                    if (StrUtil.isNotBlank(preOptionType)){
                        if (preOptionType.equals("0")){
                            //如果之前是包村组长通过过来，则可以驳回到组长
                            nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_包村组长%"));
                        }else{
                            //如果是镇长、组长驳回到领导处，直接驳回到发起人
                            //没人能委派给领导
                        }
                    }
                    //直接驳回到发起人
                    break;
                case "包村组长":
                    if (StrUtil.isNotBlank(preOptionType)){
                        if (preOptionType.equals("0")){
                            //如果之前是组员通过，则可以驳回到组员
                            nextDelegate.add(this.findFrontUser(allComment,0));
//                            nextDelegate.addAll(userService.findByDepartmentAndRoleName(securityUtil.getCurrUser().getDepartmentId(),"%包村组员%"));
                        }else if(preOptionType.equals("1")){
                            // 可以是组员驳回，可以是领导驳回.查上一步处理人操作。如果是通过，那么驳回到上一个接点人。如果是驳回或者委托，则只能是包村领导
                            String uRole = getUserRole(findFrontUser(allComment,0),matter.getField0018());
                            String action = findFrontCommentAction(allComment, 2);
                            // 包村领导的驳回特殊处理
                            if ("包村领导".equals(uRole) && "0".equals(action)) {
                                nextDelegate.add(this.findFrontUser(allComment, 2));
                            }else{
                                nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_包村领导%"));
                            }
                        }else{
                            //只有包村领导可以委托给组长
                            nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_包村领导%"));
                        }
                    }
                    break;
                case "包村组员":
                    nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_包村组长%"));
                    break;
                case "村支书":
//                    nextDelegate.addAll(userService.findByDepartmentAndRoleName(securityUtil.getCurrUser().getDepartmentId(),"%包村组员%"));
                    break;
                case "镇长":
                    //特殊处理，走流程驳回
                    ProcessNodeVo t = actProcessService.getNode(nodeId);
                    List<HistoricTaskInstance> his = historyService.createHistoricTaskInstanceQuery()
                            .processInstanceId(procInstId).orderByHistoricTaskInstanceEndTime().asc().list();
                    if (his.size() > 0){
                        HistoricTaskVo htv = new HistoricTaskVo(his.get(his.size() - 1));
                        String userId = iHistoryIdentityService.findUserIdByTypeAndTaskId(ActivitiConstant.EXECUTOR_TYPE, htv.getId());
                        for (User u :t.getUsers()){
                            if (u.getId().equals(userId)){
                                nextDelegate.add(u);
                                break;
                            }
                        }
                    }
                    break;
                case "书记":
                    nextDelegate.addAll(userService.findByRoleName("%\\_" + "南彩镇" + "_镇长%"));
                    break;
                case "科室负责人":
                    if (StrUtil.isNotBlank(preOptionType)){
                        if (preOptionType.equals("0")){
                            //如果之前是包村组长通过过来，则可以驳回到组长
                            nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_科长%"));
                        }else{
                            //如果是镇长、组长驳回到领导处，直接驳回到发起人
                            //没人能委派给领导
                        }
                    }
                    //直接驳回到发起人
                    break;
                case "科长":
                    if (StrUtil.isNotBlank(preOptionType)){
                        if (preOptionType.equals("0")){
                            //如果之前是组员通过，则可以驳回到科员
                            nextDelegate.add(this.findFrontUser(allComment,0));
//                            nextDelegate.addAll(userService.findByDepartmentAndRoleName(securityUtil.getCurrUser().getDepartmentId(),"%包村组员%"));
                        }else if(preOptionType.equals("1")){
                            // 可以是组员驳回，可以是领导驳回.查上一步处理人操作。如果是通过，那么驳回到上一个接点人。如果是驳回或者委托，则只能是包村领导
                            String uRole = getUserRole(findFrontUser(allComment,0),matter.getField0018());
                            String action = findFrontCommentAction(allComment, 2);
                            // 包村领导的驳回特殊处理
                            if ("科室负责人".equals(uRole) && "0".equals(action)) {
                                nextDelegate.add(this.findFrontUser(allComment, 2));
                            }else{
                                nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_科室负责人%"));
                            }
                        }else{
                            //只有包村领导可以委托给组长
                            nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_科室负责人%"));
                        }
                    }
                    break;
                case "科员":
                    nextDelegate.addAll(userService.findByRoleName("%\\_" + departName + "_科长%"));
                    break;
            }
        }
        if (needAddCreater){
            nextDelegate.add(userService.get(actBusiness.getUserId()));
        }
        node.setUsers(removeDuplicate(nextDelegate));
        return node;
    }

    /**
     * 去重
     * @param list
     * @return
     */
    private List<User> removeDuplicate(List<User> list) {
        LinkedHashSet<User> set = new LinkedHashSet<User>(list.size());
        set.addAll(list);
        list.clear();
        list.addAll(set);
        return list;
    }

    private String findFrontCommentAction(List<Comment> allComment,int depth){
        if (allComment.size() > (depth + 1) + 1){
            String[] optionTyps = allComment.get(allComment.size() - 1 - 2*depth - 1).getMessage().split("@_");
            if (optionTyps.length == 2){
                return optionTyps[1];
            }
            return null;
        }
        return null;
    }
    private User findFrontUser(List<Comment> allComment,int depth){
        if (allComment.size() > (depth + 1)){
            String message = allComment.get(allComment.size() - 1 - 2*depth).getMessage();
            if (message.endsWith("_owner")){
                String userId = message.split("_")[0];
                User u = userService.get(userId);
                // 关联角色
                List<Role> list = iUserRoleService.findByUserId(u.getId());
                u.setRoles(list);
                return u;
            }
            return null;
        }
        return null;
    }
}